package com.xiaokong.changerecord.impl;


import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.WeakCache;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.xiaokong.changerecord.entity.Dict;
import com.xiaokong.changerecord.enums.RedisKeyEnum;
import com.xiaokong.changerecord.strategy.DictStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.List;
import java.util.Map;
import java.util.Optional;


/**
 * 字典工具类
 *
 * @author Hu_bx
 * @date 9:40 2022/11/11
 **/
@RequiredArgsConstructor
@Slf4j
public class RedisDictImpl implements DictStrategy {


    private final StringRedisTemplate redisTemplate;
    /**
     * 二级缓存,使用弱引用内存缓存,设置超时时间,每次查询更新,当时间过期,垃圾回收机制会回收该key
     */
    private final WeakCache<String, List<Dict>> WEAK_CACHE = CacheUtil.newWeakCache(DateUnit.MINUTE.getMillis());


    @Override
    public String getDictByCode(String parentCode, String code) {
        if (StrUtil.isNullOrUndefined(code)) {
            return null;
        }
        TimeInterval timeInterval = new TimeInterval();
        timeInterval.start();
        List<Dict> dictionaryDtoList = getDictList(parentCode);
        Optional<Dict> first = dictionaryDtoList.stream()
                .filter(dictionaryDto -> dictionaryDto.getCode()
                        .equals(code))
                .findFirst();
        log.debug("常量字段耗时 {},redisKey = {}", timeInterval.intervalMs(), RedisKeyEnum.DICT_ALL_KEY.getKey() + parentCode);
        return first.map(Dict::getValue).orElse(null);
    }


    @Override
    public List<Dict> getDictList(String parentCode) {
        if (ObjUtil.isEmpty(parentCode)) {
            return CollUtil.toList();
        }
        TimeInterval timeInterval = new TimeInterval();
        timeInterval.start();

        // 内存缓存获取
        // 当涉及循环查询字典，redis还是有点慢的，短时间查询使用内存缓存
        List<Dict> dictList = WEAK_CACHE.get(parentCode);
        if (ObjUtil.isNotEmpty(dictList)) {
            log.debug("dictCache1 key = {} {}ms", parentCode, timeInterval.intervalMs());
            return dictList;
        }
        String key = RedisKeyEnum.DICT_ALL_KEY.getKey() + parentCode;
        // redis获取
        String jsonValue = redisTemplate.opsForValue().get(key);
        dictList = JSONUtil.toList(JSONUtil.isTypeJSONArray(jsonValue) ? jsonValue : "[]", Dict.class);
        // redis缓存使用
        if (ObjUtil.isNotEmpty(dictList)) {
            log.debug("dictCache2 key = {} {}ms", parentCode, timeInterval.intervalMs());
            WEAK_CACHE.put(key, dictList);
            return dictList;
        }
        return CollUtil.toList();
    }

    @Override
    public void initDict(List<Dict> dictList) {
        if (ObjUtil.isEmpty(dictList)) {
            return;
        }
        Map<String, List<Dict>> dictMap = CollStreamUtil.groupByKey(dictList, Dict::getParentCode);
        dictMap.forEach((k, v) -> {
            redisTemplate.opsForValue().set(RedisKeyEnum.DICT_ALL_KEY.getKey() + k, JSONUtil.toJsonStr(v));
            WEAK_CACHE.put(k, v);
        });
    }


}
